#include "xs_def.h"
#include "xs_tsqueue.h"

#include "xs_malloc.h"

#define ITEMS_PER_ALLOC 32

xs_queue *xs_queue_create()
{
    	xs_queue *cq = (xs_queue *) xs_malloc(sizeof(xs_queue));
    	xs_queue_initilization(cq);
	return cq;
}

void xs_queue_destroy(xs_queue *cq)
{
	if(!cq)
		return;
	xs_pthread_mutex_destroy(&cq->lock);
	xs_pthread_mutex_destroy(&cq->__freelist_lock);
	xs_free(cq);
}

void xs_queue_initilization(xs_queue *cq)
{
	if(!cq)
		return;
	
	xs_pthread_mutex_init(&cq->lock, NULL);
	cq->head = NULL;
	cq->tail = NULL;
	cq->__freelist = NULL;
	xs_pthread_mutex_init(&cq->__freelist_lock, NULL);
	cq->count = 0;
}

xs_queue_item *xs_queue_item_new(xs_queue *cq)
{
	xs_queue_item *item = NULL;

	if(!cq)
		return NULL;

	xs_pthread_mutex_lock(&cq->__freelist_lock);

	if (cq->__freelist) {
		item = cq->__freelist;
		cq->__freelist = (xs_queue_item*)item->next;
	}

	xs_pthread_mutex_unlock(&cq->__freelist_lock);

	if (NULL == item) {

		int i;

		item = (xs_queue_item *) xs_malloc(sizeof(xs_queue_item) * ITEMS_PER_ALLOC);
		if (NULL == item){
			return NULL;
		}

		for (i = 2; i < ITEMS_PER_ALLOC; i++)
		item[i - 1].next = (xs_queue_item*)(&item[i]);

		xs_pthread_mutex_lock(&cq->__freelist_lock);

		item[ITEMS_PER_ALLOC - 1].next = (xs_queue_item*)cq->__freelist;
		cq->__freelist = &item[1];

		xs_pthread_mutex_unlock(&cq->__freelist_lock);
	}

	return item;
}

void xs_queue_item_free(xs_queue *cq, xs_queue_item *item)
{
	if(!cq || !item)
		return;
	
	xs_pthread_mutex_lock(&cq->__freelist_lock);
	item->next = cq->__freelist;
	cq->__freelist = (xs_queue_item*)item;
	xs_pthread_mutex_unlock(&cq->__freelist_lock);
}

xs_queue_item *xs_queue_head(xs_queue *cq)
{
	if(!cq)
		return NULL;
	
	return cq->head;
}

xs_queue_item *xs_queue_tail(xs_queue *cq)
{
	if(!cq)
		return NULL;
	
	return cq->tail;
}

xs_queue_item *xs_queue_peek(xs_queue *cq)
{
    	return xs_queue_head(cq);
}

xs_queue_item *xs_queue_dequeue(xs_queue *cq)
{
    	xs_queue_item *item;
	
	if(!cq)
		return NULL;

	xs_pthread_mutex_lock(&cq->lock);
	item = cq->head;
	if(NULL != item){
		cq->head = (xs_queue_item*)item->next;
		if(NULL == cq->head)
			cq->tail = NULL;
		cq->count--;
	}
	xs_pthread_mutex_unlock(&cq->lock);

	return item;
}

void *xs_queue_dequeue_data(xs_queue *cq)
{
	void *data;
    	xs_queue_item *item;
	
	if(!cq)
		return NULL;
	item = xs_queue_dequeue(cq);
	if(!item){
		return NULL;
	}
	
	data = item->data;
    	xs_queue_item_free(cq, item);

	return data;
}

void xs_queue_enqueue(xs_queue *cq, xs_queue_item *item)
{
	if(!cq || !item)
		return;

	item->next = NULL;

	xs_pthread_mutex_lock(&cq->lock);
	
	if (NULL == cq->tail)	/* This is a first item */
		cq->head = item;
	else
		cq->tail->next = (xs_queue_item*)item;	/* Add item to the tail */

	cq->tail = item;
	cq->count++;

	xs_pthread_mutex_unlock(&cq->lock);
}

int xs_queue_enqueue_data(xs_queue *cq, void *data)
{
    	xs_queue_item *item;

	if(!cq || !data)
		return -1;

    	item = xs_queue_item_new(cq);
	if(!item){
		return -1;
	}

	item->data = data;
	xs_queue_enqueue(cq, item);

	return 0;
}

unsigned xs_queue_count(xs_queue *cq)
{
	return cq->count;
}

BOOL xs_queue_is_empty(xs_queue *cq)
{
	return cq->count? TRUE : FALSE;
}
